﻿// Copyright (c) 2012 - 2020 Jared Zheng <jaredz at outlook dot com>
// Buddha Framework is licensed under Mulan PSL v2.
// You can use this software according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of Mulan PSL v2 at:
//          http://license.coscl.org.cn/MulanPSL2
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
// See the Mulan PSL v2 for more details.
//
// Create : 2012-12-01 version 0.1 Jared Zheng <jaredz at outlook dot com>
// Update : 2018-12-05 version 3.1.5 Jared Zheng <jaredz at outlook dot com>

#ifndef __CENTER_SERVER_IMP_H__
#define __CENTER_SERVER_IMP_H__

#pragma once

#include "commonpacket.h"
#include "CommonServer.h"
#include "CommonRoutine.h"

///////////////////////////////////////////////////////////////////////////////////////////////////////////
// 同步管理服务器 :
// 1. 同步登陆流程相关服务器信息
// 2. 服务器信息的统计报表以及异常预警
class CCenterServer : public ICommonServer, public CPAKHandler
{
public:
    CCenterServer(void);
    virtual ~CCenterServer(void);
    // CComponent
    // Command to set param value
    virtual UInt   Command(PCXStr pszCMD, uintptr_t utParam) OVERRIDE;
    virtual UInt   Update(void) OVERRIDE;

    virtual UInt   Init(CEventHandler& EventHandlerRef, CServerConfig& ConfigRef) OVERRIDE;
    virtual void   Exit(void) OVERRIDE;

    virtual bool   Start(void) OVERRIDE;
    virtual bool   Pause(bool bPause = true) OVERRIDE;
    virtual void   Stop(void) OVERRIDE;
    // CPAKHandler
    virtual bool   OnTcpDispatch(const PacketPtr& PktPtr, PTCP_PARAM pTcp) OVERRIDE;
    virtual bool   OnUdpDispatch(const PacketPtr& PktPtr, PUDP_PARAM pUdp) OVERRIDE;//
    virtual bool   OnTcpAccept(Socket sAccept, Socket sListen) OVERRIDE;
    virtual bool   OnTcpConnect(UInt uError, Socket sConnect) OVERRIDE;//
    virtual bool   OnTcpRecv(size_t stSize, PTCP_PARAM pTcp) OVERRIDE;//
    virtual bool   OnTcpSend(UInt uQueueLimit, Socket sSocket) OVERRIDE;//
    virtual bool   OnTcpClose(Socket sSocket, ULLong ullLiveData) OVERRIDE;

    virtual bool   OnUdpRecv(size_t stSize, PUDP_PARAM pUdp) OVERRIDE;//
    virtual bool   OnUdpSend(UInt uQueueLimit, Socket sSocket) OVERRIDE;//
    virtual bool   OnUdpClose(Socket sSocket, ULLong ullLiveData) OVERRIDE;//
    // Routine
    virtual Socket_t          GetSocket(UInt uType) OVERRIDE;
    virtual PSERVER_INFO      GetServerInfo(UInt uType) OVERRIDE;
    virtual PSVR_COMMON_MAP   GetServerMap(UInt uType) OVERRIDE;
    virtual PRESULT_CHAIN     GetServerSort(UInt uType) OVERRIDE;
    virtual CServerConfig&    GetConfig(void) OVERRIDE;
    virtual CFileLog&         GetFileLog(void) OVERRIDE;
    virtual CEventHandlerPtr& GetMgrHandler(void) OVERRIDE;
    virtual CNetworkPtr&      GetNetworkPtr(void) OVERRIDE;
private:
    CCenterServer(const CCenterServer&);
    CCenterServer& operator=(const CCenterServer&);
    /// 配置相关
    bool  LoadConfig(void);
    void  UnloadConfig(void);

    bool  StartListen(void);
    bool  CheckListen(void);
    void  StopListen(void);

    /// 同步服务器信息
    bool  SyncServersInfo(void);
    /// 同步同步管理服务器信息给控制台
    bool  SyncCenterServerInfo(void);
    /// 同步授权均衡服务器信息给控制台
    bool  SyncBalanceServerInfo(void);
    /// 同步授权数据服务器信息给控制台
    bool  SyncAuthorDBServerInfo(void);
    /// 同步授权网关服务器信息给所有授权均衡服务器和控制台
    bool  SyncAuthorServerInfo(void);
    /// 同步业务数据服务器信息给控制台
    bool  SyncMasterDBServerInfo(void);
    /// 同步业务管理服务器信息给所有授权网关服务器和控制台
    bool  SyncMasterServerInfo(void);

    /// 同步授权网关服务器特殊信息给单个授权均衡服务器
    bool  SendAuthorServerInfo(Socket sSocket);
    /// 同步业务管理服务器特殊信息给单个授权网关服务器
    bool  SendMasterServerInfo(Socket sSocket);

    bool  SendAuthorServerInfo(PINDEX& index);
    bool  SendMasterServerInfo(PINDEX& index);

    bool  SendAuthorServerInfo(PINDEX& index, Socket sSocket);
    bool  SendMasterServerInfo(PINDEX& index, Socket sSocket);

    bool  OnServerLink(CNETLink* pLink, Socket sSocket);
    bool  OnServerUpdate(CNETUpdate* pUpdate, Socket sSocket);
    bool  OnServerUnlink(CPAKHeadBase* pUnlink, Socket sSocket, SERVER_UNLINK& su);
    /// 注册服务器数据
    template <typename MAP_REF, PAK_TYPE PakType>
    bool  ServerLink(CNETLink* pLink, ULLong ullKey, MAP_REF& MapRef);
    /// 更新服务器数据
    template <typename MAP_REF, PAK_TYPE PakType>
    bool  ServerUpdate(CNETUpdate* pUpdate, ULLong ullKey, MAP_REF& MapRef);
    /// 注销服务器数据
    template <typename MAP_REF, PAK_TYPE PakType>
    bool  ServerUnlink(Socket sSocket, MAP_REF& MapRef, SERVER_UNLINK& su);

    bool  LoadExtConfig(void);
    bool  LoadExtRoutine(CKVNode& Root);
    bool  CreateRTTIObject(PCXStr pszObject, PCXStr pszName, PCXStr pszModule);
    bool  UnloadExtConfig(void);
    bool  UnloadExtRoutine(void);
private:
    Int                    m_nStatus;                 ///< 服务器状态
    // 框架例程开关, 由框架例程通过调用Command修改服务器实例对应参数
    Int                    m_nServerInfo;             ///< 例程需要处理服务器同步信息PAKE_LINK/PAKE_UPDATE/PAKE_UNLINK的SERVER_INFO
    Int                    m_nCommonEvent;            ///< 例程需要处理服务器内部通用PAKE_COMMON_BEGIN/PAKE_COMMON_END事件
    //
    CServerConfig*         m_pConfig;                 ///< 配置
    Socket                 m_sListenInternal;         ///< 监听
    CFileLog               m_FileLog;                 ///< 简单文本日志
    CEventHandlerPtr       m_MgrHandler;              ///< 控制台回调接口
    CNetworkPtr            m_NetworkPtr;              ///< 网络
    CCommonRoutinePtr      m_RoutinePtr;              ///< 例程处理
    SERVER_INFO            m_Server[PAKT_MASTER + 1]; ///< 服务器组统计数据
    SVR_BALANCE_MAP        m_Balance;                 ///< 授权均衡服务器信息
    SVR_COMMON_MAP         m_AuthorDB;                ///< 授权数据服务器信息
    SVR_AUTHOR_MAP         m_Author;                  ///< 授权网关服务器信息
    SVR_COMMON_MAP         m_MasterDB;                ///< 业务数据服务器信息
    SVR_MASTER_MAP         m_Master;                  ///< 业务管理服务器信息
};

INLINE CCenterServer::CCenterServer(void)
: m_nStatus(STATUSS_NONE)
, m_nServerInfo(FALSE)
, m_nCommonEvent(FALSE)
, m_pConfig(nullptr)
, m_sListenInternal(0)
, m_FileLog(false)
{
}

INLINE CCenterServer::~CCenterServer(void)
{
}

INLINE CCenterServer::CCenterServer(const CCenterServer&)
: m_nStatus(STATUSS_NONE)
, m_nServerInfo(FALSE)
, m_nCommonEvent(FALSE)
, m_pConfig(nullptr)
, m_sListenInternal(0)
, m_FileLog(false)
{
}

INLINE CCenterServer& CCenterServer::operator=(const CCenterServer&)
{
    return (*this);
}

INLINE bool CCenterServer::OnUdpDispatch(const PacketPtr&, PUDP_PARAM)
{
    // 1. PUDP_PARAM->pCache != nullptr时上层可以保存网络层申请的缓存数据
    //    PUDP_PARAM->pCache, PUDP_PARAM->index是网络层申请的缓存数据的起始地址和索引号
    // 2. PUDP_PARAM->pData为当前数据开始地址, PUDP_PARAM->stSize为当前数据大小
    // 3. 如果上层保存缓存, 设置PUDP_PARAM->pData为nullptr通知网络层不要再使用当前缓存
    // 4. 释放缓存使用MObject::sMCFree(index, pCache)
    return false;
}

INLINE bool CCenterServer::OnTcpConnect(UInt, Socket)
{
    return false;
}

INLINE bool CCenterServer::OnTcpRecv(size_t, PTCP_PARAM)
{
    return false;
}

INLINE bool CCenterServer::OnTcpSend(UInt, Socket)
{
    return true;
}

INLINE bool CCenterServer::OnUdpRecv(size_t, PUDP_PARAM)
{
    return false;
}

INLINE bool CCenterServer::OnUdpSend(UInt, Socket)
{
    return true;
}

INLINE bool CCenterServer::OnUdpClose(Socket, ULLong)
{
    return false;
}

INLINE Socket_t CCenterServer::GetSocket(UInt uType)
{
    return 0;
}

INLINE PSERVER_INFO CCenterServer::GetServerInfo(UInt uType)
{
    PSERVER_INFO pInfo = (m_Server + PAKT_CENTER);
    switch (uType)
    {
    case PAKT_BALANCE:
    case PAKT_AUTHORDB:
    case PAKT_AUTHOR:
    case PAKT_MASTERDB:
    case PAKT_MASTER:
        {
            pInfo = (m_Server + uType);
        }
        break;
    default:
        {
        }
    }
    return pInfo;
}

INLINE PSVR_COMMON_MAP CCenterServer::GetServerMap(UInt uType)
{
    PSVR_COMMON_MAP pMap = nullptr;
    switch (uType)
    {
    case PAKT_BALANCE:
        {
            pMap = (PSVR_COMMON_MAP)&m_Balance;
        }
        break;
    case PAKT_AUTHORDB:
        {
            pMap = (PSVR_COMMON_MAP)&m_AuthorDB;
        }
        break;
    case PAKT_AUTHOR:
        {
            pMap = (PSVR_COMMON_MAP)&m_Author;
        }
        break;
    case PAKT_MASTERDB:
        {
            pMap = (PSVR_COMMON_MAP)&m_MasterDB;
        }
        break;
    case PAKT_MASTER:
        {
            pMap = (PSVR_COMMON_MAP)&m_Master;
        }
        break;
    default:
        {
        }
    }
    return pMap;
}

INLINE PRESULT_CHAIN CCenterServer::GetServerSort(UInt)
{
    return nullptr;
}

INLINE CServerConfig& CCenterServer::GetConfig(void)
{
    return (*m_pConfig);
}

INLINE CFileLog& CCenterServer::GetFileLog(void)
{
    return m_FileLog;
}

INLINE CEventHandlerPtr& CCenterServer::GetMgrHandler(void)
{
    return m_MgrHandler;
}

INLINE CNetworkPtr& CCenterServer::GetNetworkPtr(void)
{
    return m_NetworkPtr;
}

template <typename MAP_REF, PAK_TYPE PakType>
INLINE bool CCenterServer::ServerLink(CNETLink* pLink, ULLong ullKey, MAP_REF& MapRef)
{
    PINDEX index = nullptr;
    // 1.获取信息
    typename MAP_REF::SVR_PAIR Pair;
    Pair.ullKey = ullKey;
    Pair.Value.Copy(pLink->GetServerStatus());
    assert(Pair.Value.usStatus == STATUSU_LINK);
    assert(pLink->CheckStream());
    Pair.Value.Addr(pLink->GetStream());
    {
        CSyncLockWaitScope scope(MapRef.SvrLock);
        typename MAP_REF::SVR_MAP_PAIR* pPair = MapRef.SvrMap.Find(ullKey);
        if (pPair == nullptr)
        {
            index = MapRef.SvrMap.Add(ullKey, Pair.Value);
            LOGV_INFO(m_FileLog, TF("[同步管理]服务器(type=%d)%p注册1, Id=%d"), PakType, ullKey, Pair.Value.usId);
        }
        else if (pPair->m_V.usStatus == STATUSU_UNLINK)
        {   // 注销的信息未同步到其他服务器
            pPair->m_V = Pair.Value;
            index = reinterpret_cast<PINDEX>(pPair);
            LOGV_INFO(m_FileLog, TF("[同步管理]服务器(type=%d)%p注册2, Id=%d"), PakType, ullKey, Pair.Value.usId);
        }
        else
        {
            Pair.Value.usStatus = pPair->m_V.usStatus;
        }
    }
    if (index != nullptr)
    {
        m_Server[PakType].Incr(pLink->GetServerStatus());
        m_Server[PAKT_CENTER].IncrOnline();
        // 2.更新控制台
        m_MgrHandler->OnHandle(PAKE_LINK, reinterpret_cast<uintptr_t>(&Pair), PakType);
    }
    else
    {
        LOGV_WARN(m_FileLog, TF("[同步管理]服务器(type=%d)%p注册时存在异常状态=%d"), PakType, ullKey, Pair.Value.usStatus);
    }
    return (index != nullptr);
}

template <typename MAP_REF, PAK_TYPE PakType>
INLINE bool CCenterServer::ServerUpdate(CNETUpdate* pUpdate, ULLong ullKey, MAP_REF& MapRef)
{
    UShort usStatus = STATUSU_NONE;
    typename MAP_REF::SVR_PAIR Pair;
    Pair.ullKey = ullKey;
    {
        CSyncLockWaitScope scope(MapRef.SvrLock);
        typename MAP_REF::SVR_MAP_PAIR* pPair = MapRef.SvrMap.Find(ullKey);
        if ((pPair != nullptr) &&
            (pPair->m_V.usStatus > STATUSU_NONE) &&
            (pPair->m_V.usStatus < STATUSU_UNLINK))
        {
            Pair.Value.Copy(pPair->m_V);
            pPair->m_V.Copy(pUpdate->GetServerStatus());
        }
        else if (pPair != nullptr)
        {
            usStatus = pPair->m_V.usStatus;
        }
    }
    if (Pair.Value.usStatus > STATUSU_NONE)
    {
        m_Server[PakType].Diff(pUpdate->GetServerStatus(), Pair.Value);
        Pair.Value.Copy(pUpdate->GetServerStatus());
        m_MgrHandler->OnHandle(PAKE_UPDATE, reinterpret_cast<uintptr_t>(&Pair), PakType);// 更新控制台
        return true;
    }
    if (usStatus != STATUSU_NONE)
    {
        LOGV_WARN(m_FileLog, TF("[同步管理]服务器(type=%d)%p更新时存在异常状态=%d"), PakType, ullKey, usStatus);
    }
    else
    {
        LOGV_WARN(m_FileLog, TF("[同步管理]服务器(type=%d)%p更新时不存在"), PakType, ullKey);
    }
    return false;
}

template <typename MAP_REF, PAK_TYPE PakType>
INLINE bool CCenterServer::ServerUnlink(Socket sSocket, MAP_REF& MapRef, SERVER_UNLINK& su)
{
    // 2.更新控制台
    m_MgrHandler->OnHandle(PAKE_UNLINK, (uintptr_t)sSocket, PakType);
    SERVER_STATUS ss;
    {
        CSyncLockWaitScope scope(MapRef.SvrLock);
        typename MAP_REF::SVR_MAP_PAIR* pPair = MapRef.SvrMap.Find((ULLong)sSocket);
        if (pPair != nullptr)
        {
            ss = pPair->m_V;
            LOGV_INFO(m_FileLog, TF("[同步管理]服务器(type=%d)%p注销, Id=%d"), PakType, sSocket, ss.usId);
            // 3.修改状态
            if ((PakType == PAKT_BALANCE)  ||
                (PakType == PAKT_AUTHORDB) ||
                (PakType == PAKT_MASTERDB) ||
                (pPair->m_V.usStatus <= STATUSU_LINK))
            {
                // 授权均衡, 授权数据和业务数据不需要同步到其他服务器, 直接删除
                MapRef.SvrMap.RemoveAt(reinterpret_cast<PINDEX>(pPair));// 服务器从注册到注销, 信息还未同步到其他服务器, 直接作为无状态信息删除
            }
            else if ((pPair->m_V.usStatus == STATUSU_OKAY) || (pPair->m_V.usStatus == STATUSU_SYNC))
            {
                pPair->m_V.usStatus = STATUSU_UNLINK; // 需要同步给其他服务器以后再删除
            }
        }
    }
    if (ss.usStatus > STATUSU_NONE)
    {
        m_Server[PakType].Decr(ss);
        m_Server[PAKT_CENTER].DecrOnline();

        su.tiData.usType = (UShort)PakType;
        su.tiData.usId   = ss.usId;
        return true;
    }
    else
    {
        LOGV_WARN(m_FileLog, TF("[同步管理]服务器(type=%d)%p注销时不存在"), PakType, sSocket);
    }
    return false;
}

#endif // __CENTER_SERVER_IMP_H__
